Screen Collection + Checked radiobutton issue

Feb 15, 2011 at 7:40 PM

Hi, I'm trying to create a sort of wizard control by using a usercotrol with a content control that allow to attach other usercontrol into it. I'm using a List<Screen> to add the views I'm going to activate with the next, previous button. I'm having a problem with some pages which I'm using a radiobutton into it because if I select some option in the first page and click next and select other option in the second view and again click back, the option selected in the first view is lost, it doesn't happen if I use a checkbox in the second view for instance. What can I do to preserve the selected option in the previous views if I use checkbox's. There's the code I'm using:

 

MainView.xaml

    <Grid x:Name="LayoutRoot">
        <uc:WizardControlView cal:Bind.Model="{Binding}">
        </uc:WizardControlView>                   
    </Grid>    


MainView.cs

public MainViewModel(List<Screen> Myviews)
        {
            pages = Myviews.Count;

            if (pages > 0)
            {
                Cpage = 0;
                ActivateItem(Myviews[Cpage]);
            }
            comprobarPaginaActual();
        }

 public void Next()
{
   ...
   Cpage++;
  ActivateItem(Myviews[Cpage]);
  ...
}

public void Back()
{
   ...
   Cpage--;
  ActivateItem(Myviews[Cpage]);
  ...
}


Page1View.xaml

<ListBox ItemsSource="{Binding Elements, Mode=TwoWay}"
                             x:Name="ListBox1" SelectionMode="Multiple">

            <ListBox.ItemTemplate>
                <DataTemplate>
                    <RadioButton x:Name="CbPag1"  Content="{Binding Text}" IsChecked="True" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

Page1ViewModel.cs

        public Page1ViewModel()
        {
            Elements.Add(new CBElement { Id = 1, Text = "Opcion1" });
            Elements.Add(new CBElement { Id = 2, Text = "Opcion2" });
            Elements.Add(new CBElement { Id = 3, Text = "Opcion3" });
        }

Page2View.xaml

<ListBox ItemsSource="{Binding Elements2, Mode=TwoWay}"
                             x:Name="ListBox2" SelectionMode="Multiple">

            <ListBox.ItemTemplate>
                <DataTemplate>
                    <RadioButton x:Name="CbPag1"  Content="{Binding Text}" IsChecked="True" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>


Page2ViewModel.cs

        public Page2ViewModel()
        {
            Elements2.Add(new CBElement { Id = 1, Text = "Opcion1" });
            Elements2.Add(new CBElement { Id = 2, Text = "Opcion2" });
            Elements2.Add(new CBElement { Id = 3, Text = "Opcion3" });
        }

Apr 11, 2011 at 2:19 PM
Edited Apr 11, 2011 at 2:25 PM

Hi Edgar,

Did you manage to find a work around for this issue? We are having exactly the same problem.

Rob,

Are you aware of this issue?

Regards

Ildiko

Apr 12, 2011 at 3:50 PM

I'm not really sure of the root cause of the issue; my guess is that the view is recreated at every page switch, thus causing the selection to be reset.

Generally speaking, when using MVVM is better to have all the relevant view state baked by a corresponding state in the VM, so that the view could be completely recreated through binding.
There may be some legitimate exception to the "rule": for example, view specific state that is -usually- not relevant to the application logic (the position of a scroll bar).
On the contrary, I believe that at least the user input that the application needs to handle in some way *must* be represented in the VM.
It is, indeed, the preferred way to access these input from the VM side. Plus, this approach greatly helps in scenarios where part of the visual has to be recreated (which is, for example, the case for the native WPF tab control).

That being said, Caliburn.Micro has a mechanism  to address the unwanted repeated creation of multiple view instances: it preserves the view instance in a cache held by the corresponding VM, as long it implements IViewAware interface (or inherits from Screen).
This way, when the view is located for a VM, the infrastructure peeks the existing instance (if any).

 

Apr 13, 2011 at 7:19 AM

Hi Marco,

In our case, I'm working with Ildiko, we do keep all the state in the VM. And, IIRC, our views are descending from Screen. Our radiobuttons in the view are bound to boolean properties in the VM and when we've stepped through our VM code whilst returning to the view/page that contains the radiobuttons something external to our VM sets all the databound boolean properties to false. We've not managed to pinpoint where this happening or what is doing it but it does seems to be happening in CM. 

Regards

Barry Carr

Apr 13, 2011 at 9:44 AM

If you can build a small repro of the issue I could have a look into it; it's likely I'm missing something about your scenario, hence my hypotesys about view caching might not apply to it. You can send it at marco dot amendola at gmail dot com

In the meantime, I played around the SimpleMDI sample, which should be similar to the your scenario, I guess.
I modified the TabView markup as such: 

 

<UserControl x:Class="Caliburn.Micro.SimpleMDI.TabView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="This is the view for " />
            <TextBlock x:Name="DisplayName" />
            <TextBlock Text="." />

        </StackPanel>
        <CheckBox Content="xxxxx"></CheckBox>
        <CheckBox Content="yyyyy"></CheckBox>
        <RadioButton Content="1" />
        <RadioButton Content="2" />
        <RadioButton Content="3" />
    </StackPanel>
</UserControl>
    

When I switch between different tabs, the view state is kept.


Might your issue have to do with the radiobutton semantic (mutually exclusive selection)?
If I've got it correctly, you seem to describe the radiobuttons in your view as independently bound (just like a checkbox) which is usually not the case.

Apr 13, 2011 at 9:53 AM

my small guess - try to use ChangeActiveItem(Myviews[Cpage], false) instead of ActivateItem(Myviews[Cpage]);

Apr 14, 2011 at 7:48 AM

Hi Marco,

Interesting point about the radiobuttons being bound individually, I'll investigate that further. BTW, how do you bind a group of radio buttons? Thanks

Regards

Barry Carr

Apr 14, 2011 at 9:35 AM

Using RadioButton.GroupName allows the developer to define different contextes.

I am not absolutely sure (did not check this), but it seems to me that if the group is not specified, the 'checking context' depends on the visual grouping (if you place radio buttons in different panels, or group boxes, they do not share the same 'checking context').

Apr 14, 2011 at 11:28 AM
Edited Apr 14, 2011 at 11:30 AM

I think BladeWise is true about radio group scoping.

My usual way to deal with multiple RadioButton is to retemplate a ListBox.
The actual semantic expressed by a group of RadioButtons is the selection of exaclty one item among other ones: but this is exaclty the behaviour of a ListBox.
So I just use a simple ListBox, bound as usual, with a style altering its template, thus representing the selection of an item with checked RadioButton instead of the background highlight.

Also, this allows you to express the selection in the VM with a single property.

There are plenty samples of this approach around; also, I wrote a small article about it some time ago.
Unfortunately, it is in Italian. You can get the (automatically translated) version here: http://translate.google.it/translate?js=n&prev=_t&hl=en&ie=UTF-8&layout=2&eotf=1&sl=it&tl=en&u=http%3A%2F%2Fwww.domusdotnet.org%2Fpillole%2F20101001-creare-un-radiolistbox-in-wpf.aspx