Problem with regions!

Topics: Prism v2 - Silverlight 3
May 10, 2010 at 2:38 PM
Edited May 10, 2010 at 2:59 PM

Hello I have a region in my Silverlight application that looks like this:

<sdk:DataGrid IsReadOnly="True" ItemsSource="{Binding MyObject}" AutoGenerateColumns="False"
     localCommands:DataGridSelectionChanged.Command="{Binding OnSelectionChanged}">
     <sdk:DataGrid.Columns>
          <sdk:DataGridTextColumn Header="MyHeader" Binding="{Binding MyHeader}"  />
      </sdk:DataGrid.Columns>
      <sdk:DataGrid.RowDetailsTemplate>
           <DataTemplate>
                 <ContentControl  regions:RegionManager.RegionName="MyContainerRegion" />
           </DataTemplate>
     </sdk:DataGrid.RowDetailsTemplate>
 </sdk:DataGrid>
 In my event that gets called for OnSelectionChanged, I have code that looks like this:
public void DataGridSelectionChanged(object obj) 
{ 
     var region = _regionManager.Regions["MyContainerRegion"]; 
}


However I keep getting exception:
System.Collections.Generic.KeyNotFoundException was unhandled by user code Message=The region manager does not contain the MyContainerRegion region.

 

So can anyone tell me what I'm doing wrong? FYI I'm using Silverlight 4 and Prism 2.1.

 

Thanks!

May 11, 2010 at 4:02 PM
None else can tell what I'm doing wrong?
Developer
May 11, 2010 at 7:49 PM

Hi,

The RegionManager can’t find the region you’ve defined because it’s located inside a DataTemplate, and the FindRegionManager method inside the RegionManagerRegistrationBehavior cannot find regions inside DataTemplates. You can find a more detailed explanation on this thread.

On the other hand, even if you do follow the workaround explained on the aforementioned thread, you would be adding several regions with the same name under the same RegionManager, which isn’t allowed either. Depending on your scenario, a possible workaround for that would be to add the regions programmatically using scoped regions. You can find more information about scoped regions on this article.

I hope you find this helpful.

Guido Leandro Maliandi
http://blogs.southworks.net/gmaliandi

May 12, 2010 at 6:59 PM

guidomaliandi thanks for the response!  Pretty much what I'm trying to do is plug in a different views depending on if MyObject is a Business or Individual.  Each view is a DataGrid that shows the correct data depending on  the type of object.

 

Anyway I'm going to read about scoped regions that you gave me.

May 13, 2010 at 7:06 PM
Edited May 13, 2010 at 9:06 PM

I've tried using the custom RegionBeheavior approach but it seems like it's impossible to work up the tree when your in a template.  I guess templates never have a parent?  I thought of maybe putting a region at the DataGrid level and working down into the template and adding all regions in the template to the region manager.  However I think their may be problems with that approach too?  I also tried doing this:

 

<sdk:DataGrid IsReadOnly="True" ItemsSource="{Binding MyObject}" AutoGenerateColumns="False"
     localCommands:DataGridSelectionChanged.Command="{Binding OnSelectionChanged}"
     localCommands:DataGridSelectionChanged.CommandParameter="{Binding ElementName=contentControler}">
     <sdk:DataGrid.Columns>
          <sdk:DataGridTextColumn Header="MyHeader" Binding="{Binding MyHeader}"  />
      </sdk:DataGrid.Columns>
      <sdk:DataGrid.RowDetailsTemplate>
           <DataTemplate>
	       <ContentControl x:Name="contentControler" />
            </DataTemplate>
     </sdk:DataGrid.RowDetailsTemplate>
 </sdk:DataGrid>
and code behind looks like this
 
public void DataGridSelectionChanged(object parameter)
{
    RegionManager.SetRegionManager(parameter as DependencyObject, _regionManager);
    RegionManager.SetRegionName(parameter as DependencyObject, "ContentRegion");
}
However parameter is always null so the localCommands:DataGridSelectionChanged.CommandParameter="{Binding ElementName=contentControler}"
is not working.  Not sure how to pass in the instance of the ContentControl to set the region?  Anyway, if anyone can help thanks!
May 14, 2010 at 12:16 PM
no one knows the answer?
May 14, 2010 at 1:54 PM

Hi Michael,

Without knowing what those Commands do it is not possible for me to reproduce your full scenario and your issue. I’ll try to interpret the situation based on your previous questions, but let me know if I’m not on target.

What you are trying to do is pass the ContentControl to some method (probably in your ViewModel), and trying to create a Region with it. I assume you are using the selection changed event because when a new row is added to a DataGrid that event is published. If this is your scenario, you will be in the same situation Guido mentioned, having more than one region with the same name under the same region manager (assuming _regionManager instance is the same).

Additionally, if you are not getting an error because there are multiple controls named “contentControler”, it could be because they are not being added to the same XAML Namescope, as templates define their own namescope. Therefore, as the ElementName binding requires elements to be in either the current “XAML namescope or the XAML namescope of the templated parent if the binding target is in a data template or control template” (as explained here), the binding would not work properly.

The question that needs to be asked is why each row should define a new region (always considering that each of the Regions would have a different name). What is the scenario that it would help you achieve? How would the views be added? I ask this, because there might be different ways around this.

For example, creating a RegionAdapter for the DataGrid could be a viable approach. Then you could define the DataGrid as a region, and the different added views could be shown in each row (similar to how an ItemsControl region works).

Please let me know if this helps.

Damian Schenkelman
http://blogs.southworks.net/dschenkelman

May 14, 2010 at 6:11 PM
Edited May 14, 2010 at 7:19 PM

dschenkelman thanks for your quick response.  What I"m trying to do is show a Grid with data and when a row is selected I want to show another Grid for the row details.  Now depending on the object behind the row, I will switch the view for the Grid row details.  So my code is something like this:

public interface  IApplicant
{}

public interface IBussiness : IApplicant
{}

public interface IIndividual : IApplicant
{}

Now my ViewModel will have something like this:

public class MyViewModel
{
     public MyViewModel()
     {
       OnSelectionChanged = new DelegateCommand<object>(DataGridSelectionChanged);
     }

     public DelegateCommand<object> OnSelectionChanged { get; set; }
     public ObservableCollection<IApplicant> Applicants { get; set; }

     public void DataGridSelectionChanged(object parameter)
     {
          if(!_regionManager.Regions.ContainsRegionWithName("ContentRegion"))
          {
             RegionManager.SetRegionManager(parameter as DependencyObject, _regionManager);
             RegionManager.SetRegionName(parameter as DependencyObject, "ContentRegion");
          }

          var region = _regionManager.Regions["ContentRegion"];
          if (myObject is IBusiness)
          {
               var view = region.GetView("BusinessView");
               if (view == null)
                   view = region.Add(new BusinessView(), "BusinessView");

               region.Activate(view);

               view = region.GetView("IndividualView");
               if (view != null)
                   region.Deactivate(view);
           }
           else
           {
               var view = region.GetView("IndividualView");
               if (view == null)
                   view = region.Add(new IndividualView(), "IndividualView");

               region.Activate(view);

               view = region.GetView("BusinessView");
               if (view != null)
                   region.Deactivate(view);
            }
        }
    }
}

<sdk:DataGrid IsReadOnly="True" ItemsSource="{Binding Applicants}" AutoGenerateColumns="False"
     localCommands:DataGridSelectionChanged.Command="{Binding OnSelectionChanged}"
     localCommands:DataGridSelectionChanged.CommandParameter="{Binding ElementName=contentController}">
     <sdk:DataGrid.Columns>
          <sdk:DataGridTextColumn Header="MyHeader" Binding="{Binding MyHeader}"  />
      </sdk:DataGrid.Columns>
      <sdk:DataGrid.RowDetailsTemplate>
           <DataTemplate>
	       <ContentControl x:Name="contentController" />
            </DataTemplate>
     </sdk:DataGrid.RowDetailsTemplate>
 </sdk:DataGrid>

So what I want to happen is, when the user clicks on the row, I want DataGridSelectionChanged  to get called(which is happening by the way) and pass it the instance of contentController(which is not happening, it's always passing null).  If contentController already has ContentRegion I will not create it but just switch the view that active.  Anyway, I'm new at this so I'm not sure if this will even work? 

I will take a look at your region adapter approach and see how that works.

 

Thanks!

May 17, 2010 at 5:33 PM

Hi Michael,

I have created a small application to show a similar functionality to the one you want. Instead of using a region in the RowDetailsTemplate, I use a value converter to determine the visibility of the controls depending on some business rules, which is something that you can probably take advantage of as well (depending on whether the currently selected item is an IIndividual or IBusiness for example). I believe this approach greatly simplifies the scenario while allowing the same functionality.

You can download it from here.

Please let me know if this helps.

Damian Schenkelman
http://blogs.southworks.net/dschenkelman

May 18, 2010 at 2:08 PM
Thanks, I'll check this out.
May 19, 2010 at 1:05 AM
Edited May 19, 2010 at 1:09 AM

Thanks for the example.  I've thought about this approach too but was trying for a more elegant solution by having a region and switching views at run time based on what type of interface.  Guess I've been trying to force it all along?

 I do have one question about your solution, why are you setting CurrentCustomer property to SelectedItem?

Anyway, thanks your help.  I think I'm just going to use the solution you gave me.