0

I'm looking for a solution to dynamically load user controls based on the radio button selection. The problem is explained below. 1. I have a base window xaml (MainWindow). This has 2 Stack Panels, SP1 and SP2.
2. I have created 5 User Controls 2.1 Radio UC ==> 4 radio buttons. 2.2 Option-1 UC 2.3 Option-2 UC 2.4 Option-3 UC 2.5 Option-4 UC

  1. The Radio UC is loaded in SP1 of the MainWindow in the design time.
  2. Based on the selection of the Radio button, one of the 4 Option UC should be loaded dynamically in SP2

I have tried explaining the above in the attached image.

I'm clueless as I dont know WPF that well. Could anyone help me with this using WPF MVVM pattern.

Thanks in advance.

TechBang
  • 137
  • 1
  • 2
  • 6
  • Look at my answer to this question (there is a link to a previous answer that will also be needed within that answer) http://stackoverflow.com/questions/24762810/how-to-replace-the-details-view-when-toolbar-item-is-invoked/24770431#24770431 – Lee O. Aug 01 '14 at 14:52
  • You'll basically have the RadioButtons ViewModel raise a SelectionChanged Event and your MainViewModel will be registered to act on it. The MainViewModel will then set a property (type ViewModelBase) to the appropriate ViewModel type for your desired UserControl and DataTemplates will automatically change the UI. – Lee O. Aug 01 '14 at 14:55
  • This is the kind I'm planning to implement, but I do not know how to do this "raise a SelectionChanged Event and your MainViewModel will be registered to act on it. The MainViewModel will then set a property (type ViewModelBase)". Will you be able to provide code snippet. Thanks. – TechBang Aug 02 '14 at 20:35

2 Answers2

0

You could try something like this:

in XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <local:VisibilityConverter x:Key="visibilityConverter"  />
        </Grid.Resources>
        <StackPanel>
            <StackPanel>
                <RadioButton Content="one" GroupName="a" Name="one"/>
                <RadioButton Content="two" GroupName="a" Name="two"/>
            </StackPanel>
            <StackPanel >
                <RadioButton Content="linked to one" Visibility="{Binding ElementName=one, Path=IsChecked, Converter={StaticResource visibilityConverter}}"/>
                <RadioButton Content="linked to two" Visibility="{Binding ElementName=two, Path=IsChecked, Converter={StaticResource visibilityConverter}}"/>
            </StackPanel>            
        </StackPanel>
    </Grid>
</Window>

and in code the visibilityconverter:

public class VisibilityConverter : IValueConverter
{
    object IValueConverter.Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if ((Boolean)value)
        {
            return Visibility.Visible;
        }
        else
        {
            return Visibility.Collapsed;
        }
    }

    object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

I am sure your XAMl will look a bit different but the concepts will be the same. I hope.

Core-One
  • 466
  • 2
  • 6
  • 19
  • Thanks for the response, but this may not work as I have mentioned, the radio buttons are from different user control. I would have to handle this from the parent main window view model, that I dont know. – TechBang Aug 01 '14 at 14:39
  • There is no must to go through your viewmodel. If you add a dependency property to your usercontrol. You can replace the binding that is now on visibilty to your own custom property and get the selected radiobutton info into your usercontrol. This way you keep view things on view and XAML level. Is more clean in my opinion. – Core-One Aug 01 '14 at 15:02
0

In your MainViewModel create a property to hold the RadioControlViewModel and one to hold the selected OptionViewModel. In your constructor, register for the OptionSelectionChanged event of the RadioControlViewModel. The EventHandler method should change the value of OptionViewModel to an instance of a ViewModel representing the control you wish to show for that selection. All comments in code stating "etcetera" means to add the same preceeding code for option 3 & 4.

MainViewModel

public class MainViewModel
{
    private RadioControlViewModel _rcViewModel;
    public RadioControlViewModel RcViewModel
    {
       get { return _rcViewModel; }
       set
       {
          _rcViewModel = value;
          RaisePropertyChanged("RcViewModel");
       }
    }

    private OptionViewModelBase _optionViewModel;
    public OptionViewModelBase OptionViewModel
    {
       get { return _optionViewModel; }
       set
       {
          _optionViewModel = value;
          RaisePropertyChanged("OptionViewModel");
       }
    }

    public MainViewModel()
    {
       RcViewModel = new RadioControlViewModel();
       RcViewModel.OptionSelectionChanged += RcViewModel_OptionSelectionChanged;
    }

    private void RcViewModel_OptionSelectionChanged(object sender, OptionSelectionChangedEventArgs e)
    {
       if (e.Selection == null)
       {
          OptionViewModel = null;
          return;
       }

       switch (e.Selection)
       {
          case Option.OptionOne:
             OptionViewModel = new OptionOneViewModel();
             break;
          case Option.OptionTwo:
             OptionViewModel = new OptionTwoViewModel();
             break;
          // etcetera
       }
    }
 }

In your MainView.Resources, you'll place DataTemplates mapping each OptionViewModel to the corresponding OptionView. The first StackPanel has your RadioControlView with it's DataContext bound to the RadioControlViewModel property of your MainViewModel. In the second StackPanel, you use a ContentPresenter and bind the Content property to the OptionViewModel property of the MainViewModel. The DataTemplates will tell it how to present this.

MainView

 <Window.Resources>
    <DataTemplate DataType="{x:Type viewmodel:OptionOneViewModel}">
       <view:OptionOneView />
    </DataTemplate>

    <DataTemplate DataType="{x:Type viewmodel:OptionTwoViewModel}">
       <view:OptionTwoView />
    </DataTemplate>

    <!-- etcetera -->
 </Window.Resources>

 <Grid>
    <StackPanel>
       <view:RadioControlView DataContext="{Binding RcViewModel}" />
    </StackPanel>
    <StackPanel>
       <ContentPresenter Content="{Binding OptionViewModel}" />
    </StackPanel>
 </Grid>

The RadioControlViewModel will have a OptionSelectionChanged event that it raises whenever one of the bound RadioButtons set their property to true.

RadioControlViewModel

public class RadioControlViewModel
{
   public event EventHandler<OptionSelectionChangedEventArgs> OptionSelectionChanged;

   private bool _optionOneSelected;
   public bool OptionOneSelected
   {
      get { return _optionOneSelected; }
      set
      {
         _optionOneSelected = value;
         RaisePropertyChanged("OptionOneSelected");
         if (value)
            RaiseOptionSelectionChanged(Option.OptionOne);
      }
   }

   private bool _optionTwoSelected;
   public bool OptionTwoSelected
   {
      get { return _optionTwoSelected; }
      set
      {
         _optionTwoSelected = value;
         RaisePropertyChanged("OptionTwoSelected");
         if (value)
            RaiseOptionSelectionChanged(Option.OptionTwo);
      }
   }

   // etcetera

   private void RaiseOptionSelectionChanged(Option selection)
   {
      var handler = OptionSelectionChanged;
      if (handler == null)
         return;

      handler(this, new OptionSelectionChangedEventArgs(selection));
   }
}

The preceeding code is assuming that you'll create an OptionSelectionChangedEventArgs class and have an enum called Option. There will be some tweaks needed such as your MainViewModel constructor will probably want to set the OptionViewModel property to whichever Option will initialize as selected.

Lee O.
  • 3,212
  • 2
  • 26
  • 36